package com.wekri.wechat4j.util;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
import com.wekri.wechat4j.api.response.NewsResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author liuweiguo.
 */
public class XmlUtil {
    static Logger logger = LoggerFactory.getLogger(XmlUtil.class);

    public static <T> T parseObject(Reader reader, Class<T> clazz) {
        JAXBContext jc;
        try {
            jc = JAXBContext.newInstance(clazz);
            Unmarshaller u = jc.createUnmarshaller();
            Object obj = u.unmarshal(reader);
            return (T) obj;
        } catch (JAXBException e) {
            logger.error("", e);
        }
        return null;
    }

    public static <T> T parseObject(String str, Class<T> clazz) {
        return parseObject(new StringReader(str), clazz);
    }

    public static String toXmlStr(Object obj) {
        if (null == obj) {
            return null;
        }
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());

            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            // 去掉报文头
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

            List<String> nodeList = new ArrayList<>();
            Class<? extends Object> clazz = obj.getClass();
            List<Field> list = new ArrayList<>();
            list.addAll(Arrays.asList(clazz.getDeclaredFields()));
            //获取所有父类的字段
            while (clazz != Object.class) {
                clazz = clazz.getSuperclass();
                list.addAll(Arrays.asList(clazz.getDeclaredFields()));
            }

            List<Field> childFieldList = new ArrayList<>();
            for (Field field : list) {
                if (field.getType() != String.class) {
                    childFieldList.addAll(Arrays.asList(field.getType().getDeclaredFields()));
                }
            }
            list.addAll(childFieldList);

            for (Field field : list) {
                CDATA annotation = field.getAnnotation(CDATA.class);
                if (null != annotation) {
                    XmlElement xmlElement = field.getAnnotation(XmlElement.class);
                    if (StringUtils.isNotEmpty(annotation.value())) {
                        nodeList.add("^" + annotation.value());
                    } else if (null != xmlElement) {
                        nodeList.add("^" + xmlElement.name());
                    } else {
                        nodeList.add("^" + field.getName());
                    }
                }
            }
            if (obj instanceof NewsResponse) {
                nodeList.add("^Title");
                nodeList.add("^Description");
                nodeList.add("^PicUrl");
                nodeList.add("^Url");
            }
            String[] strings = new String[nodeList.size()];

            OutputStream os = new ByteArrayOutputStream();
            XMLSerializer serializer = getXMLSerializer(os, nodeList.toArray(strings));

            marshaller.marshal(obj, serializer.asContentHandler());
            return os.toString();
        } catch (Exception e) {
            logger.error("", e);
        }
        return null;
    }

    /**
     * 设置属性
     *
     * @param os
     * @return
     */
    private static XMLSerializer getXMLSerializer(OutputStream os, String[] cdataNode) {
        OutputFormat of = new OutputFormat();
        of.setCDataElements(cdataNode);
        of.setPreserveSpace(true);
        of.setIndenting(true);
        of.setOmitXMLDeclaration(true);
        XMLSerializer serializer = new XMLSerializer(of);
        serializer.setOutputByteStream(os);
        return serializer;
    }

}
